#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/led.h>
#include <linux/key.h>
#include <linux/int.h>
#include <linux/uart.h>
#include <lib/stdio.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/fs2.h>
#include <linux/signal.h>
#include <linux/preempt.h>
#include <linux/lcd.h>
#include <linux/lcdapi.h>
#include <linux/ft5xx6.h>
#include <linux/gt9147.h>
#include <linux/gfp.h>

#include <asm-generic/sections.h>

wait_queue_head_t wq;
int wq_flag = 0;
void kernel_key(void)
{
	int keyvalue = 0;

	set_task_comm(current, "[kernel_key]");

 	while (1) {
		keyvalue = key_getvalue();
		if (keyvalue) {
			switch ((keyvalue)) {
			case KEY0_VALUE:
				printf("this is %s(): %d\r\n", __func__, __LINE__);
				// send_signal(6, SIGINT);
				wq_flag = 1;
				wake_up(&wq);
				break;
			}
 		}
 	}
}

void kernel_int(void)
{
	tty_init();

	while (1) {
		tty_int_handler();
		// schedule();
	}
}

void kernel_led(void)
{
	unsigned int flag = 1;

	set_task_comm(current, "[kernel_led]");

	// preempt_disable();
	while (1) {
		if (flag)
			led_switch(LED0,0);
		else
			led_switch(LED0,1);
		flag = !flag;
		delayms(100);
		// schedule();
	}
	// preempt_enable_no_resched();

	do_exit();
}

void kernel_lcd1(void)
{
	unsigned char index = 0;
	unsigned int backcolor[10] = {
		LCD_BLUE, 		LCD_GREEN, 		LCD_RED, 	LCD_CYAN, 	LCD_YELLOW, 
		LCD_LIGHTBLUE, 	LCD_DARKBLUE, 	LCD_WHITE, 	LCD_BLACK, 	LCD_ORANGE

	}; 

	set_task_comm(current, "[kernel_lcd1]");

	tftlcd_dev.forecolor = LCD_RED;	  

	lcd_show_string(600,70,240,32,32,(char*)"INDEX=");
	while (1) {	
	    index++;
		if (index == 10)
			index = 0;      
		lcd_shownum(696,70, index, 2, 32);
		lcd_fill(500, 100, 800, 200, backcolor[index]);
	
		delayms(100);
		// schedule();
		wq_flag = 0;
		wait_event(wq, wq_flag);
	}

	do_exit();
}

void kernel_lcd2(void)
{
	unsigned char index = 0;
	unsigned int backcolor[10] = {
		LCD_BLUE, 		LCD_GREEN, 		LCD_RED, 	LCD_CYAN, 	LCD_YELLOW, 
		LCD_LIGHTBLUE, 	LCD_DARKBLUE, 	LCD_WHITE, 	LCD_BLACK, 	LCD_ORANGE

	}; 

	set_task_comm(current, "[kernel_lcd2]");

	tftlcd_dev.forecolor = LCD_RED;	  

	lcd_show_string(600,210,240,32,32,(char*)"INDEX=");
	while (1) {	
	    index++;
		if (index == 10)
			index = 0;      
		lcd_shownum(696, 210, index, 2, 32);
		lcd_fill(500, 240, 800, 340, backcolor[index]);
		
		delayms(100);
	}

	do_exit();
}

void kernel_lcd3(void)
{
	unsigned char index = 0;
	unsigned int backcolor[10] = {
		LCD_BLUE, 		LCD_GREEN, 		LCD_RED, 	LCD_CYAN, 	LCD_YELLOW, 
		LCD_LIGHTBLUE, 	LCD_DARKBLUE, 	LCD_WHITE, 	LCD_BLACK, 	LCD_ORANGE

	}; 

	set_task_comm(current, "[kernel_lcd3]");

	tftlcd_dev.forecolor = LCD_RED;	  

	lcd_show_string(600,340,240,32,32,(char*)"INDEX=");
	while (1) {	
	    index++;
		if (index == 10)
			index = 0;      
		lcd_shownum(696, 340, index, 2, 32);
		lcd_fill(500, 370, 800, 470, backcolor[index]);
		
		delayms(100);
	}

	do_exit();
}

void kernel_lcd4(void)
{
	set_task_comm(current, "[kernel_lcd4]");

	tftlcd_dev.forecolor = LCD_RED;	  

	lcd_show_string(250, 130, 150, 16, 16, (char *)"jiffies:");  
	while (1) {	
		lcd_shownum(250 + 50, 130, jiffies, 10, 16);
		// delayms(10);
	}

	do_exit();
}

void kernel_touchscreen(void)
{
	set_task_comm(current, "[kernel_touchscreen]");

	tftlcd_dev.forecolor = LCD_RED;
	lcd_show_string(50, 10, 400, 24, 24, (char*)"IMX6U-ALPHA TOUCH SCREEN TEST");  
	lcd_show_string(50, 40, 200, 16, 16, (char*)"TOUCH SCREEN TEST");  
	lcd_show_string(50, 60, 200, 16, 16, (char*)"ATOM@ALIENTEK");  
	lcd_show_string(50, 80, 200, 16, 16, (char*)"2019/3/27");  
	
	lcd_show_string(50, 110, 400, 16, 16,	(char*)"TP Num	:");  
	lcd_show_string(50, 130, 200, 16, 16,	(char*)"Point0 X:");  
	lcd_show_string(50, 150, 200, 16, 16,	(char*)"Point0 Y:");  
	lcd_show_string(50, 170, 200, 16, 16,	(char*)"Point1 X:");  
	lcd_show_string(50, 190, 200, 16, 16,	(char*)"Point1 Y:");  
	lcd_show_string(50, 210, 200, 16, 16,	(char*)"Point2 X:");  
	lcd_show_string(50, 230, 200, 16, 16,	(char*)"Point2 Y:");  
	lcd_show_string(50, 250, 200, 16, 16,	(char*)"Point3 X:");  
	lcd_show_string(50, 270, 200, 16, 16,	(char*)"Point3 Y:");  
	lcd_show_string(50, 290, 200, 16, 16,	(char*)"Point4 X:");  
	lcd_show_string(50, 310, 200, 16, 16,	(char*)"Point4 Y:");  
	tftlcd_dev.forecolor = LCD_BLUE;
	
	while(1)					
	{
		if(gt_init_fail==1) {
			lcd_shownum(50 + 72, 110, ft5426_dev.point_num , 1, 16);
			lcd_shownum(50 + 72, 130, ft5426_dev.x[0], 5, 16);
			lcd_shownum(50 + 72, 150, ft5426_dev.y[0], 5, 16);
			lcd_shownum(50 + 72, 170, ft5426_dev.x[1], 5, 16);
			lcd_shownum(50 + 72, 190, ft5426_dev.y[1], 5, 16);
			lcd_shownum(50 + 72, 210, ft5426_dev.x[2], 5, 16);
			lcd_shownum(50 + 72, 230, ft5426_dev.y[2], 5, 16);
			lcd_shownum(50 + 72, 250, ft5426_dev.x[3], 5, 16);
			lcd_shownum(50 + 72, 270, ft5426_dev.y[3], 5, 16);
			lcd_shownum(50 + 72, 290, ft5426_dev.x[4], 5, 16);
			lcd_shownum(50 + 72, 310, ft5426_dev.y[4], 5, 16);
		} else {
			lcd_shownum(50 + 72, 110, gt9147_dev.point_num , 1, 16);
			lcd_shownum(50 + 72, 130, gt9147_dev.x[0], 5, 16);
			lcd_shownum(50 + 72, 150, gt9147_dev.y[0], 5, 16);
			lcd_shownum(50 + 72, 170, gt9147_dev.x[1], 5, 16);
			lcd_shownum(50 + 72, 190, gt9147_dev.y[1], 5, 16);
			lcd_shownum(50 + 72, 210, gt9147_dev.x[2], 5, 16);
			lcd_shownum(50 + 72, 230, gt9147_dev.y[2], 5, 16);
			lcd_shownum(50 + 72, 250, gt9147_dev.x[3], 5, 16);
			lcd_shownum(50 + 72, 270, gt9147_dev.y[3], 5, 16);
			lcd_shownum(50 + 72, 290, gt9147_dev.x[4], 5, 16);
			lcd_shownum(50 + 72, 310, gt9147_dev.y[4], 5, 16);
		}
		delayms(10);
	}

	do_exit();
}

static void run_init_process(char *init_filename)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	kernel_execve(init_filename, 0, 0);
}

static int init_post(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	run_init_process("/bin/bash");

	printf("No init found.  Try passing init= option to kernel.");
	while (1);
}

void kernel_init(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	init_post();
}

int kthreadd(void)
{
	struct task_struct *tsk = current;

	printf("this is %s(): %d\r\n", __func__, __LINE__);

	set_task_comm(tsk, "[kthreadd]");
	init_waitqueue_head(&wq);

	kernel_thread(kernel_led, 0, 0);
	// kernel_thread(kernel_int, 0, 0);
	kernel_thread(kernel_key, 0, 0);
	kernel_thread(kernel_lcd1, 0, 0);
	kernel_thread(kernel_lcd2, 0, 0);
	kernel_thread(kernel_lcd3, 0, 0);
	kernel_thread(kernel_lcd4, 0, 0);
	kernel_thread(kernel_touchscreen, 0, 0);

	while (1) {
		// printf("this is %s(): %d\r\n", __func__, __LINE__);
		delayms(100);
		// schedule();
	}

	return 0;
}

void cpu_idle(void)
{
	while (1) {
		printf("this is %s(): %d\r\n", __func__, __LINE__);
		delayms(100);
		// schedule();
	}
}

static void rest_init(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	kernel_thread(kernel_init, 0, 0);
	// kernel_thread(kthreadd, 0, 0);
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	preempt_enable_no_resched();
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	schedule();
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	preempt_disable();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	cpu_idle();
}

static void mm_init(void)
{
	// page_cgroup_init_flatmem();
	mem_init();
	// kmem_cache_init();
	// pgtable_cache_init();
	// vmalloc_init();
}

#include <asm/map.h>
#include <asm/memory.h>
static void map_io(void)
{
	struct map_desc map;

#if 0
	/* 
	 * gic and other device addresses have been mapped in head.S
	 */
	// gic
	map.pfn = __phys_to_pfn(0xa00000);
	map.virtual = 0xa00000;
	map.length = 2 * 1024 * 1024;
	map.type = MT_UNCACHED;
	create_mapping(&map);

	map.pfn = __phys_to_pfn(0xb00000);
	map.virtual = 0xb00000;
	map.length = 1 * 1024 * 1024;
	map.type = MT_UNCACHED;
	create_mapping(&map);

	// devices
	map.pfn = __phys_to_pfn(0x02000000);
	map.virtual = 0x02000000;
	map.length = 10 * 1024 * 1024;
	map.type = MT_UNCACHED;
	create_mapping(&map);
#endif

	// lcd fb
	map.pfn = __phys_to_pfn(0x9FE00000);
	map.virtual = 0xDFE00000;
	map.length = 2 * 1024 * 1024;
	map.type = MT_UNCACHED;
	create_mapping(&map);
}

void start_kernel(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	local_irq_disable();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	setup_arch();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	/* 
	 * Temporary solution for mapping lcd fb, must be placed before mm_init 
	 */
	map_io();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	build_all_zonelists();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	mm_init();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	early_irq_init();
	gic_init(); 				/* 初始化中断(一定要最先调用！) */
	imx6u_clkinit();			/* 初始化系统时钟 			*/
	delay_init();				/* 初始化延时 			*/
	clk_enable();				/* 使能所有的时钟 			*/
	// show_clocks();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	// epit1_init(0, 66000000 / 2);		/* 初始化EPIT1定时器，1分频，计数值为:66000000/2，也就是定时周期为500ms */                
	// epit1_init(0, 66000000 / 2 / 10);	/* 初始化EPIT1定时器，1分频，计数值为:66000000/2/10，也就是定时周期为50ms */                
	epit1_init(0, 66000000 / 2 / 50);	/* 初始化EPIT1定时器，1分频，计数值为:66000000/2/50，也就是定时周期为10ms */                
	led_init();					/* 初始化led 			*/
	led_switch(LED0,0);
	key_init();					/* 初始化key 			*/
	uart_init();				/* 初始化串口，波特率115200 */
	lcd_init();					/* 初始化LCD 			*/
	gt9147_init();				/* 初始化触摸屏			*/
	// simple_sd_init();
	uboot_sd_init();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	init_romfs_fs();
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	init_ext2_fs();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	vfs_caches_init(0);
	printf("this is %s(): %d\r\n", __func__, __LINE__);
	radix_tree_init();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	sched_init();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	preempt_disable();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	local_irq_enable();
	printf("this is %s(): %d\r\n", __func__, __LINE__);

	rest_init();
}

void *_kmalloc(unsigned int size)
{
	int count = PAGE_SIZE;
	static int addr = 0xD4000000;

	if (size <= PAGE_SIZE) {
		addr += PAGE_SIZE;
	} else {
		while (count < size) {
			count += PAGE_SIZE;
		}
		addr += count;
	}

	return (void *)addr;
}

void *kmalloc(unsigned int size)
{
	void *addr;

	addr = _kmalloc(size);
	memset(addr, 0, size);

	return addr;
}

void *kzalloc(unsigned int size)
{
	return kmalloc(size);
}

void kfree(void *addr)
{

}

void print_reg(unsigned int r0, unsigned int r1, unsigned int r2)
{
	printf("this is %s(): %d	r0 = 0x%x, r1 =  0x%x, r2 =  0x%x\r\n", __func__, __LINE__, r0, r1, r2);
}

void enter_usr(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
}
void enter_svc(void)
{
	printf("this is %s(): %d\r\n", __func__, __LINE__);
}
